home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************/
- /* ray_polygon.c */
- /* */
- /* Ray / polygon intersection routines */
- /* */
- /* Copyright (C) 1992, Bernard Kwok */
- /* All rights reserved. */
- /* Revision 1.0 */
- /* May, 1992 */
- /**********************************************************************/
- #include <stdio.h>
- #include <math.h>
- #include "geo.h"
- #include "misc.h"
- #include "struct.h"
- #include "ray.h"
- #include "io.h"
-
- extern OptionType Option;
- extern RayStats_Type RayStats;
- extern Polygon *shootPatch;
- extern Polygon *recPatch;
-
- /**********************************************************************/
- #define Positive_side 1
- #define Negative_side 0
- #define get_side(y) (y>=0.0 ? 1 : 0) /* Find out side of poly */
-
- /**********************************************************************/
- /* Find maximal component of a vector */
- /**********************************************************************/
- #define get_max_component(v) \
- (((fabs((v).x)>fabs((v).y))&&(fabs((v).x)>fabs((v).z)))?0 \
- :(((fabs((v).y)>fabs((v).x))&&(fabs((v).y)>fabs((v).z)))?1:2))
-
- /**********************************************************************/
- /* Get u and v directions based on 2 points, and normal direction */
- /**********************************************************************/
- void get_uv(w,p1,p2,u,v)
- int w;
- Vector p1, p2;
- double *u, *v;
- {
- switch(w){
- case 0 :
- *u = p1.y-p2.y; *v = p1.z-p2.z;
- break;
- case 1 :
- *u = p1.z-p2.z; *v = p1.x-p2.x;
- break;
- case 2 :
- *u = p1.x-p2.x; *v = p1.y-p2.y;
- break;
- default:
- break;
- }
- }
-
- /**********************************************************************/
- /* Test if ray-plane intersection is inside a polygon */
- /* Project the polygon points on a co-ordinate plane. The projection */
- /* does not preserve area but topology is preserved. Use projection to*/
- /* count polygon edge crossings. If the number is odd then the point */
- /* is inside else it is outside. */
- /**********************************************************************/
- int Inside_Polygon(p,pptr)
- Vector *p; /* Intersection point */
- Polygon *pptr; /* Polygon to test */
- {
- int i,ii,max_comp;
- int first_x_status, first_y_status, second_x_status, second_y_status;
- int num_of_crossings;
- double u1, u2, v1, v2;
- int numVert;
-
- /* Find the the component with largest magnitude
- in the normal to the plane of the polygon.
- Suppose x component has largest magnitude, then plane is yz
- Compute u, v directions on this plane.
- */
- num_of_crossings = 0;
- numVert = pptr->numVert;
- max_comp = get_max_component(pptr->normal[0]);
- get_uv(max_comp,pptr->vert[0]->pos,*p,&u1, &v1);
-
- /* Traverse poly and count crossings */
- first_x_status = (u1 > 0.0);
- first_y_status = (v1 >= 0.0);
-
- for(i=1;i<=numVert;i++) {
- ii = ((i==numVert) ? 0 : i);
- get_uv(max_comp, pptr->vert[ii]->pos,
- (*p), &u2, &v2);
- second_x_status = (u2 > 0.0);
- second_y_status = (v2 >= 0.0);
-
- /* Count crossings */
- if (first_y_status != second_y_status) {
- if ((first_x_status == Positive_side) &&
- (second_x_status == Positive_side))
- num_of_crossings++;
- else if ((first_x_status == Positive_side) ||
- (second_x_status == Positive_side))
- if ((u1 - v1*(u2-u1)/(v2-v1))>0)
- num_of_crossings++;
- }
- first_x_status = second_x_status;
- first_y_status = second_y_status;
- u1 = u2; v1 = v2;
- }
- return(num_of_crossings&1); /* If odd -> is inside */
- }
-
- /**********************************************************************/
- /* Form bounding box around a polygon */
- /**********************************************************************/
- BoundingBoxType BoxPoly (poly)
- Polygon *poly;
- {
- BoundingBoxType box;
- int i;
-
- box.min.x = box.max.x = poly->vert[0]->pos.x;
- box.min.y = box.max.y = poly->vert[0]->pos.y;
- box.min.z = box.max.z = poly->vert[0]->pos.z;
-
- for (i=1; i<poly->numVert; i++) {
- if (poly->vert[i]->pos.x < box.min.x)
- box.min.x = poly->vert[i]->pos.x;
- else if (poly->vert[i]->pos.x > box.max.x)
- box.max.x = poly->vert[i]->pos.x;
- if (poly->vert[i]->pos.y < box.min.y)
- box.min.y = poly->vert[i]->pos.y;
- else if (poly->vert[i]->pos.y > box.max.y)
- box.max.y = poly->vert[i]->pos.y;
- if (poly->vert[i]->pos.z < box.min.z)
- box.min.z = poly->vert[i]->pos.z;
- else if (poly->vert[i]->pos.z > box.max.z)
- box.max.z = poly->vert[i]->pos.z;
- }
- return (box);
- }
-
- /**********************************************************************/
- /* Test if ray intersection just touches the poly edges */
- /**********************************************************************/
- int RayPoly_Edge(r, pptr)
- Vector *r;
- Polygon *pptr;
- {
- int i, ii;
- int touch = FALSE;
- Vector endpt[2];
- Vector nrm;
- static long PolyEdgeTests = 0;
- static long PolyEdgeHits = 0;
-
- PolyEdgeTests++;
- i= 0;
- nrm = pptr->normal[0];
- while((touch == FALSE) && (i < pptr->numVert)) {
- ii = (i + 1) % pptr->numVert;
- endpt[0] = pptr->vert[i]->pos;
- endpt[1] = pptr->vert[ii]->pos;
- if ((endpt[0].x <= r->x) && (r->x <= endpt[1].x))
- if ((endpt[0].y <= r->y) && (r->y <= endpt[1].y))
- if ((endpt[0].z <= r->z) && (r->z <= endpt[1].z))
- if (dot(&nrm, vsub(r,&endpt[0])) == 0.0) {
- touch = TRUE;
- PolyEdgeHits++;
- }
- if ((endpt[0].x >= r->x) && (r->x >= endpt[1].x))
- if ((endpt[0].y >= r->y) && (r->y >= endpt[1].y))
- if ((endpt[0].z >= r->z) && (r->z >= endpt[1].z))
- if (dot(&nrm, vsub(r,&endpt[0])) == 0.0) {
- touch = TRUE;
- PolyEdgeHits++;
- }
- i++;
- }
- return touch;
- }
-
- /**********************************************************************/
- /* Ray plane intersect, return distance hit if new maximum */
- /**********************************************************************/
- int RayPlane(N,d, ray, hit)
- Vector *N;
- double d;
- Ray *ray;
- HitData *hit;
- {
- double t;
- static long PlaneTests = 0;
- static long PlaneHits = 0;
- Vector v;
-
- PlaneTests++;
-
- v = ray->dir;
- norm(&v);
- t = dot(N, &v);
- if (fabs(t) < DAMN_SMALL)
- return FALSE;
- hit->distance = - (d + dot(N, &ray->origin)) / t;
- hit->normal = *N;
- PlaneHits++;
- return TRUE;
- }
-
- /**********************************************************************/
- /* Value of pt in plane equation */
- /**********************************************************************/
- #define Plane_Value(N,pt,d) (dot((N),(pt)) + (d))
-
- /**********************************************************************/
- /* Test if point is within quadralateral or triangle */
- /**********************************************************************/
- int Inside_QuadTri(pptr, pt)
- Polygon *pptr;
- Vector *pt;
- {
- int inside = FALSE;
- polyUVW *uvw;
- double pval;
-
- uvw = pptr->uvw;
-
- /* Test against U,V, W planes of traingle */
- if (pptr->class == TRIANGLE) {
- if (Plane_Value(&uvw->Nu, pt, uvw->du) >= 0.0)
- if (Plane_Value(&uvw->Nv, pt, uvw->dv) >= 0.0)
- if (Plane_Value(&uvw->Nw, pt, uvw->dw) >= 0)
- inside = TRUE;
- }
-
- /* Test against U,V planes of quadralateral */
- else {
- pval = Plane_Value(&uvw->Nu, pt, uvw->du);
- if (pval >= 0.0 && pval <= uvw->Lu) {
- pval = Plane_Value(&uvw->Nv, pt, uvw->dv);
- if (pval >= 0.0 && pval <= uvw->Lv)
- inside = TRUE;
- }
- }
- return inside;
- }
-
- /**********************************************************************/
- /* Ray polygon intersection. Return hit data, and if hit */
- /**********************************************************************/
- int RayPolygon(pptr, ray, hit, max_distance, pbox, isect_quadtri)
- Polygon *pptr;
- Ray *ray;
- HitData *hit;
- double max_distance;
- BoundingBoxType pbox;
- int isect_quadtri;
- {
- int hit_poly = FALSE;
- double box_hit_dist;
-
- /* First check if box around polygon hits ray */
- /* if (Bounds_Behind_Plane(&pbox, shootPatch->normal[0], shootPatch->d))
- return FALSE;
- */
- if (!RayBoundingBox(pbox, *ray, &box_hit_dist))
- return FALSE;
- if (box_hit_dist >= max_distance)
- return FALSE;
-
- /* Check plane to see if hits within a maximum distance.
- If so, then check if inside polygon */
- if (RayPlane(&pptr->normal[0], pptr->d, ray, hit)) {
- if ((hit->distance > 0.0) && (hit->distance < max_distance)) {
- Point_On_Ray(ray->origin,hit->distance,ray->dir,hit->intersect);
-
- if (isect_quadtri) /* Intersect only quads and triangles */
- hit_poly = Inside_QuadTri(pptr, &hit->intersect);
- else { /* General poly intersect */
- if ((hit_poly = RayPoly_Edge(&hit->intersect, pptr)) == FALSE)
- hit_poly = Inside_Polygon(&hit->intersect, pptr);
- }
- /* Store hit info */
- if (hit_poly) {
- hit->obj = pptr->Mfather->Ofather;
- hit->mesh = pptr->Mfather;
- hit->poly = pptr;
- }
- }
- }
- return hit_poly;
- }
-
- /**********************************************************************/
- /* Ray / polygonal mesh intersection. Return hit data, and if hit */
- /**********************************************************************/
- void RayMesh(ray, hit, optr)
- register Ray *ray;
- register HitData *hit;
- register Objectt *optr;
- {
- int i,j;
- Mesh *mptr;
- Polygon *pptr;
- HitData polyhit;
- BoundingBoxType pbox;
-
- /* Check all polygons of mesh with the ray */
- mptr = optr->meshes;
- i = 0;
- while (i<optr->num_meshes && ray->visible > 0.0) {
- RayStats.rayMesh++;
- if (Option.debug)
- printf("\t\tRay-Mesh: %s%d : visible = %g\n",
- mptr->name, mptr->id, ray->visible);
- pptr = mptr->polys;
- j = 0;
- while (j<mptr->num_polys && ray->visible > 0.0) {
- if (Option.debug)
- printf("\t\tRay-Poly: %s%d : visible = %g\n", pptr->name, pptr->id,
- ray->visible);
-
- /* Find if any ray / poly intersection */
- polyhit = *hit;
- if ((pptr != shootPatch) && (pptr != recPatch)) {
- RayStats.rayPoly++;
- pbox = BoxPoly(pptr);
- if (RayPolygon(pptr, ray, &polyhit, hit->distance, pbox)) {
- if (polyhit.distance < hit->distance) {
- RayStats.intPoly++;
- ray->visible = 0.0;
- Store_HitInter(hit,polyhit.obj, polyhit.distance,
- polyhit.intersect.x, polyhit.intersect.y,
- polyhit.intersect.z);
- if (!ray->shadow)
- Store_HitNorm(hit, polyhit.normal.x, polyhit.normal.y,
- polyhit.normal.z);
- }
- }
- }
- pptr = pptr++; j++;
- }
- mptr = mptr++; i++;
- }
- }
-